﻿///<copyright>
/// Copyright © 2009 Stephen Styrchak. All Rights Reserved.
///</copyright>
using System;
using Microsoft.Xna.Framework;
using System.ComponentModel;
using System.Diagnostics;

namespace Ferpect.ComponentModel.Sprites
{
    public abstract class UpdatableSprite : Sprite, IGameComponent, IUpdatableChildComponent
    {
        private bool autoEnable;
        private bool enabled;
        private IUpdatableParentComponent parent;
        private IScreen parentScreen;
        private int updateOrder;

        public UpdatableSprite()
        {
            this.autoEnable = true;
            this.enabled = true;
        }

        #region IGameComponent Members

        public abstract void Initialize();

        #endregion

        #region IUpdatableChildComponent Members

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public IUpdatableParentComponent Parent
        {
            get { return this.parent; }
            set
            {
                if (!Object.ReferenceEquals(this.parent, value))
                {
                    if (this.parent != null)
                    {
                        if (this.autoEnable)
                        {
                            this.parent.EnabledChanged -= this.OnParentEnabledChanged;
                            this.parent.UpdateOrderChanged -= this.OnParentUpdateOrderChanged;
                        }
                        this.parent.Components.Remove(this);
                    }
                    if (this.parentScreen != null)
                    {
                        this.parentScreen.FocusChanged -= this.OnParentFocusChanged;
                    }
                }
                this.parent = value;
                if (value != null)
                {
                    if (this.autoEnable)
                    {
                        this.parent.EnabledChanged += this.OnParentEnabledChanged;
                        this.parent.UpdateOrderChanged += this.OnParentUpdateOrderChanged;
                        this.Enabled = this.parent.Enabled;
                        this.UpdateOrder = this.parent.UpdateOrder + 1;
                    }
                    if (!this.parent.Components.Contains(this))
                    {
                        this.parent.Components.Add(this);
                    }
                    this.parentScreen = this.parent as IScreen;
                    if (this.parentScreen != null)
                    {
                        this.parentScreen.FocusChanged += this.OnParentFocusChanged;
                    }
                }
                else
                {
                    if (this.autoEnable)
                    {
                        this.Enabled = false;
                    }
                }
            }
        }

        #endregion

        #region IUpdateable Members

        [Category("Behavior")]
        [Description("Indicates whether the component should be updated. If false, the Update method will not be called.")]
        [DefaultValue(true)]
        public bool Enabled
        {
            get
            {
                return this.enabled;
            }
            set
            {
                if (this.enabled != value)
                {
                    this.enabled = value;
                    this.OnEnabledChanged(this, EventArgs.Empty);
                }
            }
        }

        [Category("Property Changed")]
        public event EventHandler<EventArgs> EnabledChanged;

        public abstract void Update(GameTime gameTime);

        [Category("Behavior")]
        [Description("Indicates when the game component should be updated relative to other game components. Lower values are updated first.")]
        [DefaultValue(0)]
        public int UpdateOrder
        {
            get
            {
                return this.updateOrder;
            }
            set
            {
                if (this.updateOrder != value)
                {
                    this.updateOrder = value;
                    this.OnUpdateOrderChanged(this, EventArgs.Empty);
                }
            }
        }

        [Category("Property Changed")]
        public event EventHandler<EventArgs> UpdateOrderChanged;

        #endregion

        /// <summary>
        /// When true, this component will match its Enabled property value
        /// to that of its parent's Enabled property.
        /// </summary>
        [Category("Behavior")]
        [DefaultValue(true)]
        public bool AutoEnable
        {
            get { return this.autoEnable; }
            set
            {
                if (this.autoEnable != value)
                {
                    this.autoEnable = value;
                    // Don't sync to the parent or handle the EnableChanged event in design mode.
                    if (this.parent != null && !this.DesignMode)
                    {
                        this.Enabled = this.parent.Enabled;
                        if (this.autoEnable)
                        {
                            this.parent.EnabledChanged += this.OnParentEnabledChanged;
                        }
                        else
                        {
                            this.parent.EnabledChanged -= this.OnParentEnabledChanged;
                        }
                    }
                }
            }
        }

        protected virtual void OnParentEnabledChanged(object sender, EventArgs args)
        {
            Debug.Assert(this.autoEnable, "We should only be handling IUpdatableParentComponent.EnabledChanged if AutoEnable is true.");
            this.Enabled = this.parent.Enabled;
        }

        protected virtual void OnParentUpdateOrderChanged(object sender, EventArgs args)
        {
            Debug.Assert(this.autoEnable, "We should only be handling IUpdatableParentComponent.UpdateOrderChanged if AutoEnable is true.");
            this.UpdateOrder = this.parent.UpdateOrder + 1;
        }

        protected virtual void OnParentFocusChanged(object sender, EventArgs args)
        {
            Debug.Assert(this.parentScreen != null, "We should only be handling IScreen.FocusChanged if the parent component implements IScreen.");
        }

        protected virtual void OnEnabledChanged(object sender, EventArgs args)
        {
            if (this.EnabledChanged != null)
            {
                this.EnabledChanged(this, args);
            }
        }

        protected virtual void OnUpdateOrderChanged(object sender, EventArgs args)
        {
            if (this.UpdateOrderChanged != null)
            {
                this.UpdateOrderChanged(this, args);
            }
        }
    }
}
